package cn.xeblog.plugin.game;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.xeblog.commons.entity.User;
import cn.xeblog.commons.entity.game.GameDTO;
import cn.xeblog.commons.entity.game.GameRoom;
import cn.xeblog.commons.enums.Action;
import cn.xeblog.commons.enums.Game;
import cn.xeblog.commons.enums.UserStatus;
import cn.xeblog.plugin.action.ConsoleAction;
import cn.xeblog.plugin.action.GameAction;
import cn.xeblog.plugin.action.MessageAction;
import cn.xeblog.plugin.cache.DataCache;
import cn.xeblog.plugin.tools.AbstractPanelComponent;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.ui.components.JBScrollPane;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 游戏抽象类
 *
 * @author anlingyi
 * @date 2020/8/31
 */
public abstract class AbstractGame<T extends GameDTO> extends AbstractPanelComponent implements GameRoomEventHandler {

    /**
     * 当前游戏玩家列表
     */
    private JPanel gameUserPanel;
    /**
     * 在线用户列表面板
     */
    private JPanel onlineUserListPanel;
    /**
     * 开始游戏按钮
     */
    private JButton startGameButton;
    /**
     * 准备文本按钮
     */
    private JLabel readyLabel;
    /**
     * 检索文本
     */
    private JTextField searchUserField;
    /**
     * 游戏房间处理器
     */
    private GameRoomHandler gameRoomHandler;

    /**
     * 初始化游戏
     */
    public AbstractGame() {
        // 当玩家为发起者时，初始化游戏面板
        super(GameAction.isProactive());
        // 当前游戏
        AbstractGame abstractGame = this;

        // 游戏房间相关处理
        this.gameRoomHandler = new GameRoomHandler() {
            @Override
            protected void allPlayersGameStarted() {
                // 房间内所有玩家都已开始游戏后处理
                abstractGame.allPlayersGameStarted();
            }

            @Override
            public void roomOpened(GameRoom gameRoom) {
                // 房间已开启相关操作
                super.roomOpened(gameRoom);
                // 初始化游戏房间面板
                invoke(abstractGame::showGameRoomPanel);
            }
        };
    }

    /**
     * 开始游戏
     */
    protected final void startGame() {
        // 开始游戏，由子类实现
        this.start();
        // 控制台消息一直在底部
        ConsoleAction.gotoConsoleLow(true);
    }

    /**
     * 开始游戏
     */
    protected abstract void start();

    /**
     * 游戏数据处理
     *
     * @param body 数据内容
     */
    public void handle(T body) {
    }


    /**
     * 所有玩家游戏开始
     */
    protected void allPlayersGameStarted() {

    }

    /**
     * 获取游戏房间
     *
     * @return 游戏房间
     */
    public GameRoom getRoom() {
        return this.gameRoomHandler.gameRoom;
    }

    /**
     * 创建游戏房间
     *
     * @param game     游戏
     * @param nums     人数
     * @param gameMode 模式
     */
    private void createRoom(Game game, int nums, String gameMode) {
        this.gameRoomHandler.createRoom(game, nums, gameMode);
    }

    /**
     * 当前是否为房主
     *
     * @return true, 房主，否则，false
     */
    protected boolean isHomeowner() {
        return this.gameRoomHandler.isHomeowner;
    }

    /**
     * 设置当前是否为房主
     *
     * @param isHomeowner 是否为房主
     * @return true, 设置成功，否则，false
     */
    protected boolean setHomeowner(boolean isHomeowner) {
        return this.gameRoomHandler.isHomeowner = isHomeowner;
    }

    /**
     * 发送游戏数据
     *
     * @param body 数据内容
     */
    protected void sendMsg(GameDTO body) {
        body.setRoomId(getRoom().getId());
        MessageAction.send(body, Action.GAME);
    }

    /**
     * 设置退出按钮，并绑定结果命令
     *
     * @return 按钮组件
     */
    protected JButton getExitButton() {
        JButton exitButton = super.getExitButton();
        // 重写了按钮的文本
        exitButton.setText("退出游戏");
        return exitButton;
    }

    /**
     * 获取创建房间按钮
     *
     * @param numsList 数量
     * @return 创建房间按钮
     */
    protected JButton getCreateRoomButton(List<Integer> numsList) {
        return this.getCreateRoomButton(numsList, null);
    }

    /**
     * 获取创建房间按钮
     *
     * @param numsList     数量列表
     * @param gameModeList 游戏模式列表
     * @return 创建房间按钮
     */
    protected JButton getCreateRoomButton(List<Integer> numsList, List<String> gameModeList) {
        JButton createGameRoomButton = new JButton("创建房间");
        createGameRoomButton.addActionListener(e -> this.showCreateGameRoomPanel(numsList, gameModeList));
        return createGameRoomButton;
    }

    /**
     * 获取游戏结束的按钮，并绑定了游戏结束的监听
     *
     * @return 游戏结束的按钮
     */
    protected JButton getGameOverButton() {
        JButton exitButton = new JButton("游戏结束");
        exitButton.addActionListener(e -> gameRoomHandler.gameOver());
        return exitButton;
    }

    /**
     * 创建游戏房间面板
     *
     * @param numsList     数量列表
     * @param gameModeList 游戏模式列表
     */
    protected void showCreateGameRoomPanel(List<Integer> numsList, List<String> gameModeList) {
        new CreateGameRoomDialog(numsList, gameModeList).show();
    }

    /**
     * 创建房间对话框
     */
    private static class CreateGameRoomDialog extends DialogWrapper {

        /**
         * 主面板
         */
        private JPanel main;
        /**
         * 人数下拉列表框
         */
        private ComboBox numsBox;
        /**
         * 人数下拉列表框选项列表
         */
        private List<Integer> numsList;
        /**
         * 游戏模式下拉列表框选项列表
         */
        private List<String> gameModeList;
        /**
         * 游戏模式下拉列表框
         */
        private ComboBox gameModeBox;

        /**
         * 创建房间对话框
         *
         * @param numsList 人数下拉列表框选项列表
         */
        CreateGameRoomDialog(List<Integer> numsList) {
            this(numsList, null);
        }

        /**
         * 创建房间对话框
         *
         * @param numsList     人数下拉列表框选项列表
         * @param gameModeList 游戏模式下拉列表框
         */
        CreateGameRoomDialog(List<Integer> numsList, List<String> gameModeList) {
            // 对话框为其他窗口的父窗口
            super(true);
            // 初始化属性
            this.numsList = numsList;
            this.gameModeList = gameModeList;

            // 设置标题
            super.setTitle("创建房间");
            // 禁止调整大小
            super.setResizable(false);
            // 显示ok和取消按钮
            super.setOKActionEnabled(true);
            // 设置文本
            super.setOKButtonText("创建");
            super.setCancelButtonText("取消");

            // 初始化
            super.init();
        }

        @Override
        protected @Nullable JComponent createCenterPanel() {
            this.main = new JPanel();
            // 设置此组件的首选大小
            this.main.setPreferredSize(new Dimension(200, 50));
            Box vBox = Box.createVerticalBox();
            this.main.add(vBox);

            // 水平
            Box hBox = Box.createHorizontalBox();
            vBox.add(hBox);

            // 添加房间人数组合下拉列表框
            JLabel label = new JLabel("房间人数：");
            label.setFont(new Font("", 1, 13));
            hBox.add(label);
            this.numsBox = new ComboBox();
            this.numsList.forEach(nums -> numsBox.addItem(nums));
            hBox.add(numsBox);

            //如果游戏模式列表不为空时
            if (CollUtil.isNotEmpty(this.gameModeList)) {
                // 水平
                Box hBox2 = Box.createHorizontalBox();
                vBox.add(hBox2);

                // 添加游戏模式组合下拉列表框
                JLabel gameModelLabel = new JLabel("游戏模式：");
                gameModelLabel.setFont(new Font("", 1, 13));
                hBox2.add(gameModelLabel);

                this.gameModeBox = new ComboBox();
                this.gameModeList.forEach(mode -> gameModeBox.addItem(mode));
                hBox2.add(this.gameModeBox);
            }

            return main;
        }

        @Override
        protected void doOKAction() {
            int nums = Integer.parseInt(this.numsBox.getSelectedItem().toString());
            String gameMode = null;
            if (this.gameModeBox != null) {
                gameMode = this.gameModeBox.getSelectedItem().toString();
            }
            // 创建房间传入选择的参数
            GameAction.getAction().createRoom(GameAction.getGame(), nums, gameMode);
            super.doOKAction();
        }

    }

    /**
     * 联机游戏 - 初始化游戏房间面板
     */
    protected void showGameRoomPanel() {
        this.mainPanel.removeAll();
        // 设置可见并设置大小
        this.mainPanel.setVisible(true);
        this.mainPanel.setLayout(null);
        // 设置主面板尺寸大小
        this.mainPanel.setMinimumSize(new Dimension(300, 200));


        // 游戏房间
        GameRoom gameRoom = getRoom();

        JPanel panel = new JPanel();
        panel.setBounds(10, 10, 280, 500);
        // 标题文本
        JLabel titleLabel = new JLabel("欢迎进入【" + gameRoom.getGame().getName() + "】游戏房间~");
        titleLabel.setFont(new Font("", 1, 14));
        titleLabel.setForeground(new Color(239, 106, 106));
        // 加入面板
        panel.add(titleLabel);


        Box mainVBox = Box.createVerticalBox();

        // 加入游戏模式文本
        String gameMode = gameRoom.getGameMode();
        if (gameMode != null) {
            Box hBox = Box.createHorizontalBox();
            // 游戏模式文本框
            JLabel modeLabel = new JLabel(gameMode);
            modeLabel.setFont(new Font("", 1, 13));
            hBox.add(modeLabel);
            // 高度为10的垂直盒子
            mainVBox.add(Box.createVerticalStrut(10));
            // 加入游戏模式
            mainVBox.add(hBox);
        }

        // 水平盒子
        Box hBox = Box.createHorizontalBox();
        JLabel label1 = new JLabel("【房间玩家】");
        label1.setFont(new Font("", 1, 13));
        // 加入水平盒子
        hBox.add(label1);

        // 准备文本框
        this.readyLabel = new JLabel("准备");
        this.readyLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        this.readyLabel.setFont(new Font("", 1, 12));
        this.readyLabel.setForeground(new Color(255, 104, 104));

        // 准备文本鼠标监听
        this.readyLabel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                // 如果按钮没有禁用，点击则说明玩家游戏已经准备好了
                if (readyLabel.isEnabled()) {
                    gameRoomHandler.playerReady();
                }
            }
        });
        // 加入水平盒子
        hBox.add(this.readyLabel);

        Box vBox = Box.createVerticalBox();
        // 游戏用户列表面板
        gameUserPanel = new JPanel();
        vBox.add(gameUserPanel);
        // 刷新游戏房间用户列表
        flushGameRoomUsers();

        Box hBox2 = Box.createHorizontalBox();
        // 在线玩家文本
        JLabel label2 = new JLabel("【在线玩家】");
        label2.setFont(new Font("", 1, 13));
        // 加入水平盒子
        hBox2.add(label2);

        // 刷新文本
        JLabel flushOnlineUserLabel = new JLabel("刷新");
        flushOnlineUserLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        flushOnlineUserLabel.setFont(new Font("", 1, 12));
        flushOnlineUserLabel.setForeground(new Color(255, 104, 104));

        // 刷新在线玩家列表
        flushOnlineUserLabel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                // 刷新在线用户
                flushOnlineUsers();
            }
        });
        // 加入水平盒子
        hBox2.add(flushOnlineUserLabel);

        Box vBox2 = Box.createVerticalBox();
        // 搜索用户文本输入框
        searchUserField = new JTextField();
        searchUserField.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                // 根据过滤文本刷新在线用户
                flushOnlineUsers();
            }
        });
        // 加入水平盒子
        vBox2.add(searchUserField);

        vBox2.add(Box.createVerticalStrut(5));

        // 在线用户主面板
        JPanel onlineUserMainPanel = new JPanel(new BorderLayout());
        // 设置此组件的首选大小
        onlineUserMainPanel.setPreferredSize(new Dimension(250, 150));
        onlineUserListPanel = new JPanel();
        // 刷新在线用户
        flushOnlineUsers();
        JBScrollPane onlineUserScrollBar = new JBScrollPane(onlineUserListPanel);
        onlineUserScrollBar.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        // 加入垂直滚动条
        onlineUserMainPanel.add(onlineUserScrollBar);
        // 加入水平盒子
        vBox2.add(onlineUserMainPanel);

        Box hBox3 = Box.createHorizontalBox();
        // 如果是房主，显示开始游戏按钮
        if (isHomeowner()) {
            startGameButton = new JButton("开始游戏");
            startGameButton.setEnabled(false);
            startGameButton.addActionListener(e -> {

                gameRoomHandler.gameStart();
                startGameButton.setEnabled(false);
            });
            hBox3.add(startGameButton);

            JButton closeRoomButton = new JButton("关闭房间");
            closeRoomButton.addActionListener(e -> {
                gameRoomHandler.closeRoom();
                init();
            });
            // 关闭房间按钮
            hBox3.add(closeRoomButton);
        } else {
            // 退出游戏按钮
            hBox3.add(getExitButton());
        }

        mainVBox.add(Box.createVerticalStrut(20));
        // 房间玩家一行
        mainVBox.add(hBox);
        mainVBox.add(Box.createVerticalStrut(10));
        // 游戏玩家列表
        mainVBox.add(vBox);
        mainVBox.add(Box.createVerticalStrut(10));
        // 按钮行(开始游戏、关闭房间、退出游戏)
        mainVBox.add(hBox3);
        mainVBox.add(Box.createVerticalStrut(10));
        // 在线玩家文本一行
        mainVBox.add(hBox2);
        mainVBox.add(Box.createVerticalStrut(10));
        // 搜索框、在线玩家列表面板
        mainVBox.add(vBox2);
        // 加入面板
        panel.add(mainVBox);

        // 将面板加入主面板
        mainPanel.add(panel);
        // 更新界面
        mainPanel.updateUI();
    }

    /**
     * 获取用户状态
     *
     * @param status 用户状态对象
     * @return 用户状态值
     */
    private int getUserStatusOrder(UserStatus status) {
        switch (status) {
            case FISHING:
                return 0;
            case PLAYING:
                return 1;
            case WORKING:
                return 2;
        }
        return -1;
    }

    /**
     * 刷新用户列表
     */
    private void flushOnlineUsers() {
        // 游戏房间不存在是，跳过
        GameRoom gameRoom = getRoom();
        if (gameRoom == null) {
            return;
        }

        // 在线用户列表
        Map<String, User> onlineUserMap = DataCache.userMap;
        // 过滤后的用户列表
        List<User> userList = new ArrayList<>();
        // 搜索文本
        String search = searchUserField != null ? searchUserField.getText() : "";
        // 搜索文本为空时，显示当前在线用户列表
        if (StrUtil.isBlank(search)) {
            userList.addAll(onlineUserMap.values());
        } else {
            // 过滤用户列表
            onlineUserMap.forEach((k, v) -> {
                if (k.toLowerCase().contains(search.toLowerCase())) {
                    userList.add(v);
                }
            });
        }
        // 排序
        userList.sort((u1, u2) -> {
            int o1 = getUserStatusOrder(u1.getStatus());
            int o2 = getUserStatusOrder(u2.getStatus());
            if (o1 < o2) {
                return -1;
            }
            if (o1 == o2) {
                return 0;
            }
            return 1;
        });

        // 移除当前所有组件
        onlineUserListPanel.removeAll();
        // 垂直盒子
        Box vBox = Box.createVerticalBox();
        // 过滤后的用户列表遍历
        for (User user : userList) {
            // 房主的昵称
            if (user.getUsername().equals(GameAction.getNickname())) {
                continue;
            }

            // 用户列表面板
            JPanel userPanel = new JPanel();
            // 用户文本组件
            JLabel label = new JLabel(user.getUsername() + "(" + user.getStatus().alias() + ")");
            label.setFont(new Font("", 0, 13));
            // 文本添加
            userPanel.add(label);

            // 房主 且 用户状态为鱼 且 游戏房间未关闭
            if (isHomeowner() && user.getStatus() == UserStatus.FISHING && !gameRoom.isOvered()) {
                // 添加邀请文本组件
                JLabel inviteLabel = new JLabel("邀请");
                inviteLabel.setFont(new Font("", 1, 12));
                inviteLabel.setForeground(new Color(93, 187, 70));
                inviteLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                inviteLabel.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        // 邀请组件开启时
                        if (inviteLabel.isEnabled()) {
                            // 邀请玩家
                            gameRoomHandler.invitePlayer(user.getUsername());
                            // 禁用
                            inviteLabel.setEnabled(false);
                            // 变更文本
                            inviteLabel.setText("已邀请");
                        }
                    }
                });
                // 添加邀请面板
                userPanel.add(inviteLabel);
            }
            // 垂直盒子添加用户面板
            vBox.add(userPanel);
        }

        // 在线用户列表添加垂直盒子
        onlineUserListPanel.add(vBox);
        // 更新ui
        onlineUserListPanel.updateUI();
    }

    /**
     * 刷新游戏房间用户列表
     */
    private void flushGameRoomUsers() {
        // 游戏房间不存在是，跳过
        GameRoom gameRoom = getRoom();
        if (gameRoom == null) {
            return;
        }

        // 房间人数
        int nums = gameRoom.getNums();
        // 游戏房间内玩家列表
        List<GameRoom.Player> userList = new ArrayList<>(gameRoom.getUsers().values());
        // 当前用户数
        int currentNums = userList.size();
        // 已准备玩家的数量
        int readyCount = 0;
        Box vBox = Box.createVerticalBox();

        // 移除所有组件
        gameUserPanel.removeAll();
        for (int i = 0; i < nums; i++) {
            // 游戏房间内玩家名称颜色
            Color color = new Color(69, 232, 232);

            GameRoom.Player player = i >= currentNums ? null : userList.get(i);
            String username = null;

            // 指定下标的玩家存在时
            if (player != null) {
                username = player.getUsername();
                if (gameRoom.isHomeowner(player.getUsername())) {
                    username += " [房主]";
                }
                // 如果已经准备了，设置颜色且名称后+(OK),同时已准备数量+1
                if (player.isReadied()) {
                    readyCount++;
                    username += " (OK)";
                    color = new Color(107, 215, 80);
                }
                // 拼接用户名称
                username = (i + 1) + ". " + username;
            }

            // 用户名为空，设置颜色为灰色
            if (username == null) {
                color = new Color(171, 170, 170);
                username = "[虚位以待]";
            }
            // 设置用户名颜色，字体大小
            JLabel usernameLabel = new JLabel(username);
            usernameLabel.setForeground(color);
            usernameLabel.setFont(new Font("", 0, 13));
            vBox.add(usernameLabel);
            // 添加一个高度5的盒子
            vBox.add(Box.createVerticalStrut(5));
        }

        // 开始游戏按钮存在时，如果准备数量与房间号人数相同，启用开始游戏按钮
        if (startGameButton != null) {
            startGameButton.setEnabled(readyCount == nums);
        }
        // 添加房间玩家列表
        gameUserPanel.add(vBox);
        // 更新界面
        gameUserPanel.updateUI();
    }

    @Override
    public void roomCreated(GameRoom gameRoom) {
        // 客户端房间创建
        gameRoomHandler.roomCreated(gameRoom);
    }

    @Override
    public void roomOpened(GameRoom gameRoom) {
        // 设置客户端房间
        gameRoomHandler.roomOpened(gameRoom);
    }

    @Override
    public void roomClosed() {
        // 置空房间且 取消调度器任务并置空任务
        gameRoomHandler.roomClosed();
        // 如果是房主，房间号置空
        if (isHomeowner()) {
            GameAction.setRoomId(null);
            // 子类实现房间关闭时的页面
            invoke(() -> init());
        } else {
            // 非房主游戏结束
            GameAction.over();
        }
    }

    @Override
    public void playerJoined(User player) {
        gameRoomHandler.playerJoined(player);
        invoke(() -> {
            // 刷新游戏房间用户列表
            flushGameRoomUsers();
            // 刷新在线用户
            flushOnlineUsers();
        });
    }

    @Override
    public void playerLeft(User player) {
        // 玩家离开
        gameRoomHandler.playerLeft(player);
        invoke(() -> {
            // 刷新游戏房间用户
            flushGameRoomUsers();
            // 刷新在线用户
            flushOnlineUsers();
        });
    }

    @Override
    public void playerInviteFailed(User player) {
        // 玩家邀请失败
        gameRoomHandler.playerInviteFailed(player);
        // 刷新在线用户
        invoke(() -> flushOnlineUsers());
    }

    @Override
    public void playerReadied(User player) {
        // 设置玩家已准备
        gameRoomHandler.playerReadied(player);
        invoke(() -> {
            // 匹配昵称成功 将准备文本按钮文本变为已准备并禁用
            if (player.getUsername().equals(GameAction.getNickname())) {
                readyLabel.setEnabled(false);
                readyLabel.setText("已准备");
            }
            // 刷新游戏房间用户列表
            flushGameRoomUsers();
        });
    }

    @Override
    public void gameStarted(GameRoom gameRoom) {
        // 设置游戏房间-- 这个房间是服务端传过来的
        gameRoomHandler.gameStarted(gameRoom);
        invoke(() -> {
            // 开始游戏
            startGame();
            // 所有玩家已准备，并且房主已点击开始游戏按钮，通知服务端游戏已开始
            gameRoomHandler.playerGameStarted();
        });
    }

    @Override
    public void gameEnded() {
        // 游戏结束
        gameRoomHandler.gameEnded();
        // 展示游戏房间页
        invoke(() -> showGameRoomPanel());
    }

    @Override
    public void playerGameStarted(User user) {
        // 玩家游戏已开始，即按下开始游戏后的一瞬间，这个方法被触发
        gameRoomHandler.playerGameStarted(user);
    }
}